#include <iostream>
#include <array>

//c
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>

#define EPOLL_EXENTS_MAX_COUNT 512

int main() {
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd == -1)
        return -1;

    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERT_PORT);
    inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr);

    if(bind(socketfd, (sockaddr*)(&addr), sizeof(sockaddr_in)) == -1)
        return -1;

    if (listen(socketfd, 4) == -1)
        return -1;

    //size 为在内核中初始化多少节点, 与最大节点无关
    epoll_event event;
    event.data.fd = socketfd;   //user data
    event.events = EPOLLIN | EPOLLET;

    int epollfd = epoll_create(512);
    epoll_ctl(epollfd, EPOLL_CTL_ADD, socketfd, &event);    //EPOLL_CTL_ADD 添加 fd 以及事件

    std::array<epoll_event, EPOLL_EXENTS_MAX_COUNT> eventsCallback;
    while (true) {
        int number = epoll_wait(epollfd, eventsCallback.data(), EPOLL_EXENTS_MAX_COUNT, -1);

        for (int i = 0; i < number; i++) {
            auto& ev = eventsCallback[i];

            if (ev.data.fd == socketfd) {
                sockaddr_in client;
                socklen_t len(sizeof(sockaddr_in));
                int clientfd = accept(socketfd, (sockaddr*)(&client), &len);
                if (clientfd <= 0)
                    continue;

                epoll_event e;
                e.data.fd = clientfd;
                e.events = EPOLLIN | EPOLLET |EPOLLRDHUP;

                epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &e); 
            }

            else if (ev.events & EPOLLIN) {
                std::cout << "read from client: " << ev.data.fd << std::endl;
                char *buf = new char[MAXLINE + 1];
                //断开连接
                if (read(ev.data.fd, buf, MAXLINE) == 0) {
                    epoll_ctl(epollfd, EPOLL_CTL_DEL, ev.data.fd, nullptr); //删除 fd
                    close(ev.data.fd);
                    continue;
                }

                std::cout << buf << std::endl;
                write(ev.data.fd, buf, MAXLINE);    //echo

                //不需要更改为EPOLLOUT, 参考 EPOLLOUT 触发时机: https://cloud.tencent.com/developer/article/1481046
                //EPOLLOUT 会在写 fd 的时候触发, 下面这种用法是不正确的(对于当前的 echo 来说, 会导致消息发不出去)

                // epoll_event e;
                // e.data.fd = ev.data.fd;
                // ev.data.ptr = buf;  //存储 user data
                // e.events = EPOLLOUT | EPOLLET | EPOLLRDHUP;
                // epoll_ctl(epollfd, EPOLL_CTL_MOD, ev.data.fd, &e);  //修改fd, 从监听可读变为监听可写
            }

            // else if (ev.events & EPOLLOUT) {
            //     write(ev.data.fd, ev.data.ptr, MAXLINE); //不能发出消息
            //     delete (char*)ev.data.ptr;

            //     epoll_event e;
            //     e.data.fd = ev.data.fd;
            //     e.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
            //     epoll_ctl(epollfd, EPOLL_CTL_MOD, ev.data.fd, &e);  //更改为监听可读
            // }

            else if (ev.events & EPOLLRDHUP) {
                epoll_ctl(epollfd, EPOLL_CTL_DEL, ev.data.fd, nullptr); //删除 fd
                close(ev.data.fd);
            }
        }

    }

    return 0;
}